package com.gmp.common.utils;

import com.gmp.common.config.MinioConfig;
import com.gmp.common.exception.UtilException;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Minio工具类
 * @author Mr.Hssy
 */
@Slf4j
@Component
public class MinioUtil {
 
    @Autowired
    private MinioClient minioClient;
 
    @Autowired
    private MinioConfig configuration;
 
    /**
     * 判断bucket是否存在
     * @param name 名字
     */
    public boolean existBucket(String name) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
        } catch (Exception e) {
            log.error("MinIO异常", e);
            throw new  UtilException("MinIO异常");
        }
        return exists;
    }

 
    /**
     * 删除存储bucket
     * @param bucketName 存储bucket名称
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            log.error("MinIO 删除 存储bucket 异常", e);
            return false;
        }
        return true;
    }
 
    /**
     * 获取上传临时签名
     * @param fileName 文件名称
     * @param time     时间
     */
    @SneakyThrows
    public Map<String, String> getPolicy(String fileName, ZonedDateTime time) {
        PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
        postPolicy.addEqualsCondition("key", fileName);
        try {
            Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
            HashMap<String, String> map1 = new HashMap<>(16);
            map.forEach((k, v) -> map1.put(k.replaceAll("-", ""), v));
            map1.put("host", configuration.getUrl() + "/" + configuration.getBucketName());
            return map1;
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                 InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
                 XmlParserException e) {
            log.error("MinIO异常", e);
        }
        return null;
    }
 
    /**
     * 获取上传文件的url
     * @param objectName 对象名称
     * @param method     方法
     * @param time       时间
     * @param timeUnit   时间单位
     */
    public String getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(method)
                    .bucket(configuration.getBucketName())
                    .object(objectName)
                    .expiry(time, timeUnit).build());
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                 InvalidResponseException | IOException | NoSuchAlgorithmException | XmlParserException |
                 ServerException e) {
            log.error("MinIO异常", e);
        }
        return null;
    }
 
 
    /**
     * 上传文件
     * @param file     文件
     * @param fileName 文件名称
     */
    // -------------------------- 支持自定义桶名的上传方法 --------------------------
    public void upload(String bucketName, MultipartFile file, String fileName) {
        try (InputStream inputStream = file.getInputStream()) {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName) // 用前端传入的桶名
                    .object(fileName)   // 生成的唯一文件名
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType()) // 保持文件原有类型
                    .build());
        } catch (Exception e) {
            log.error("上传文件到桶【{}】失败，文件名：{}", bucketName, fileName, e);
            throw new RuntimeException("文件上传失败：" + e.getMessage());
        }
    }
 
    /**
     * 根据filename获取文件访问地址
     * @param objectName 对象名称
     * @param time       时间
     * @param timeUnit   时间单位
     */
    // -------------------------- 支持自定义桶名的URL获取方法 --------------------------
    public String getFileUrl(String bucketName, String objectName) {
        try {
            // 1. MinIO访问端点（从配置文件读取更灵活，这里直接示例）
            String endpoint = configuration.getUrl();

            // 2. 处理端点尾部的斜杠，避免重复
            if (endpoint.endsWith("/")) {
                endpoint = endpoint.substring(0, endpoint.length() - 1);
            }

            // 3. 关键修改：取消URL编码，直接使用原始中文文件名
            // （MinIO支持中文文件名，无需编码即可访问）
            String encodedObjectName = objectName;

            // 4. 拼接完整URL：端点 + 桶名 + 原始中文文件名
            return String.format("%s/%s/%s", endpoint, bucketName, encodedObjectName);
        } catch (Exception e) {
            log.error("获取文件URL失败，bucketName: {}, objectName: {}", bucketName, objectName, e);
            return null;
        }
    }
 
    /**
     * 下载文件
     * @param fileName 文件名称 服务器上的
     * @param defineName 文件名称 下载后的
     * @param browserShow 是否浏览器显示
     * @param mediaType 文件格式
     * <p>Code bY Mr.Hongtao ❤</p>
     */
    public ResponseEntity<byte[]> download(String fileName, String defineName, boolean browserShow, MediaType mediaType) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                if (browserShow) {
                    // 浏览器打开
                    headers.add("Content-Disposition", "inline;filename=" + URLEncoder.encode(defineName == null ? fileName : defineName, "UTF-8"));
                    headers.add(HttpHeaders.CONTENT_TYPE, mediaType + ";charset=utf-8");
 
                }else {
                    headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(defineName == null ? fileName : defineName, "UTF-8"));
                    headers.setContentType(org.springframework.http.MediaType.APPLICATION_OCTET_STREAM);
                }
            } catch (UnsupportedEncodingException e) {
                log.error("MinIO异常", e);
            }
            headers.setContentLength(bytes.length);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            log.error("MinIO异常", e);
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        log.error("MinIO异常", e);
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                log.error("MinIO异常", e);
            }
        }
        return responseEntity;
    }
 
    /**
     * @param objectFile 对象文件
     */
    public String getFileUrl(String objectFile) {
        try {
 
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(configuration.getBucketName())
                    .object(objectFile)
                    .build()
            );
        } catch (Exception e) {
            log.error("MinIO异常", e);
        }
 
        return null;
    }
}